/**
 * WYSIWYG - jQuery plugin 0.5
 *
 * Copyright (c) 2008-2009 Juan M Martinez
 * http://plugins.jquery.com/project/jWYSIWYG
 *
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 * $Id: $
 */
(function($) {
    $.fn.document = function() {
        var element = this[0];

        if (element.nodeName.toLowerCase() == 'iframe') {
            return element.contentWindow.document;
        }
        /*
         return ( $.browser.msie )
         ? document.frames[element.id].document
         : element.contentWindow.document // contentDocument;
         */
        else {
            return $(this);
        }
    };

    $.fn.documentSelection = function() {
        var element = this[0];

        if (element.contentWindow.document.selection) {
            return element.contentWindow.document.selection.createRange().text;
        }
        else {
            return element.contentWindow.getSelection().toString();
        }
    };

    $.fn.wysiwyg = function(options) {
        if (arguments.length > 0 && arguments[0].constructor == String) {
            var action = arguments[0].toString();
            var params = [];

            for (var i = 1; i < arguments.length; i++)
                params[i - 1] = arguments[i];

            if (action in Wysiwyg) {
                return this.each(function() {
                    $.data(this, 'wysiwyg')
                        .designMode();

                    Wysiwyg[action].apply(this, params);
                });
            }
            else {
                return this;
            }
        }

        var controls = {};

        /**
         * If the user set custom controls, we catch it, and merge with the
         * defaults controls later.
         */
        if (options && options.controls) {
            var controls = options.controls;
            delete options.controls;
        }

        var options = $.extend({
                                   html : '<' + '?xml version="1.0" encoding="UTF-8"?'
                                       + '><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">STYLE_SHEET</head><body>INITIAL_CONTENT</body></html>',
                                   css  : {},

                                   debug        : false,

                                   autoSave     : true,  // http://code.google.com/p/jwysiwyg/issues/detail?id=11
                                   rmUnwantedBr : true,  // http://code.google.com/p/jwysiwyg/issues/detail?id=15
                                   brIE         : true,

                                   controls : {},
                                   messages : {}
                               }, options);

        options.messages = $.extend(true, options.messages, Wysiwyg.MSGS_EN);
        options.controls = $.extend(true, options.controls, Wysiwyg.TOOLBAR);

        for (var control in controls) {
            if (control in options.controls) {
                $.extend(options.controls[control], controls[control]);
            }
            else {
                options.controls[control] = controls[control];
            }
        }

        // not break the chain
        return this.each(function() {
            Wysiwyg(this, options);
        });
    };

    function Wysiwyg(element, options) {
        return this instanceof Wysiwyg
            ? this.init(element, options)
            : new Wysiwyg(element, options);
    }

    $.extend(Wysiwyg, {
        insertImage : function(szURL, attributes) {
            var self = $.data(this, 'wysiwyg');

            if (self.constructor == Wysiwyg && szURL && szURL.length > 0) {
                if (attributes) {
                    self.editorDoc.execCommand('insertImage', false, '#jwysiwyg#');
                    var img = self.getElementByAttributeValue('img', 'src', '#jwysiwyg#');

                    if (img) {
                        img.src = szURL;

                        for (var attribute in attributes) {
                            img.setAttribute(attribute, attributes[attribute]);
                        }
                    }
                }
                else {
                    self.editorDoc.execCommand('insertImage', false, szURL);
                }
            }
        },

        createLink : function(szURL) {
            var self = $.data(this, 'wysiwyg');

            if (self.constructor == Wysiwyg && szURL && szURL.length > 0) {
                var selection = $(self.editor).documentSelection();

                if (selection.length > 0) {
                    self.editorDoc.execCommand('unlink', false, []);
                    self.editorDoc.execCommand('createLink', false, szURL);
                }
                else if (self.options.messages.nonSelection) {
                    alert(self.options.messages.nonSelection);
                }
            }
        },

        setContent : function(newContent) {
            var self = $.data(this, 'wysiwyg');
            self.setContent(newContent);
            self.saveContent();
        },

        clear : function() {
            var self = $.data(this, 'wysiwyg');
            self.setContent('');
            self.saveContent();
        },

        MSGS_EN : {
            nonSelection : 'select the text you wish to link'
        },

        TOOLBAR : {
            bold          : { visible : true, tags : ['b', 'strong'], css : { fontWeight : 'bold' } },
            italic        : { visible : true, tags : ['i', 'em'], css : { fontStyle : 'italic' } },
            strikeThrough : { visible : false, tags : ['s', 'strike'], css : { textDecoration : 'line-through' } },
            underline     : { visible : false, tags : ['u'], css : { textDecoration : 'underline' } },

            separator00 : { visible : false, separator : true },

            justifyLeft   : { visible : false, css : { textAlign : 'left' } },
            justifyCenter : { visible : false, tags : ['center'], css : { textAlign : 'center' } },
            justifyRight  : { visible : false, css : { textAlign : 'right' } },
            justifyFull   : { visible : false, css : { textAlign : 'justify' } },

            separator01 : { visible : false, separator : true },

            indent  : { visible : false },
            outdent : { visible : false },

            separator02 : { visible : false, separator : true },

            subscript   : { visible : false, tags : ['sub'] },
            superscript : { visible : false, tags : ['sup'] },

            separator03 : { visible : false, separator : true },

            undo : { visible : false },
            redo : { visible : false },

            separator04 : { visible : false, separator : true },

            insertOrderedList    : { visible : false, tags : ['ol'] },
            insertUnorderedList  : { visible : false, tags : ['ul'] },
            insertHorizontalRule : { visible : false, tags : ['hr'] },

            separator05 : { separator : true },

            createLink : {
                visible : true,
                exec    : function() {
                    var selection = $(this.editor).documentSelection();

                    if (selection.length > 0) {
                        if ($.browser.msie) {
                            this.editorDoc.execCommand('createLink', true, null);
                        }
                        else {
                            var szURL = prompt('URL', 'http://');

                            if (szURL && szURL.length > 0) {
                                this.editorDoc.execCommand('unlink', false, []);
                                this.editorDoc.execCommand('createLink', false, szURL);
                            }
                        }
                    }
                    else if (this.options.messages.nonSelection) {
                        alert(this.options.messages.nonSelection);
                    }
                },

                tags : ['a']
            },

            insertImage : {
                visible : true,
                exec    : function() {
                    if ($.browser.msie) {
                        this.editorDoc.execCommand('insertImage', true, null);
                    }
                    else {
                        var szURL = prompt('URL', 'http://');

                        if (szURL && szURL.length > 0) {
                            this.editorDoc.execCommand('insertImage', false, szURL);
                        }
                    }
                },

                tags : ['img']
            },

            separator06 : { separator : true },

            h1mozilla : { visible : true
                && $.browser.mozilla, className : 'h1', command : 'heading', arguments : ['h1'], tags : ['h1'] },
            h2mozilla : { visible : true
                && $.browser.mozilla, className : 'h2', command : 'heading', arguments : ['h2'], tags : ['h2'] },
            h3mozilla : { visible : true
                && $.browser.mozilla, className : 'h3', command : 'heading', arguments : ['h3'], tags : ['h3'] },

            h1 : { visible : true
                && !( $.browser.mozilla ), className : 'h1', command : 'formatBlock', arguments : ['Heading 1'], tags : ['h1'] },
            h2 : { visible : true
                && !( $.browser.mozilla ), className : 'h2', command : 'formatBlock', arguments : ['Heading 2'], tags : ['h2'] },
            h3 : { visible : true
                && !( $.browser.mozilla ), className : 'h3', command : 'formatBlock', arguments : ['Heading 3'], tags : ['h3'] },

            separator07 : { visible : false, separator : true },

            cut   : { visible : false },
            copy  : { visible : false },
            paste : { visible : false },

            separator08 : { separator : true && !( $.browser.msie ) },

            increaseFontSize : { visible : true && !( $.browser.msie ), tags : ['big'] },
            decreaseFontSize : { visible : true && !( $.browser.msie ), tags : ['small'] },

            separator09 : { separator : true },

            html : {
                visible : false,
                exec    : function() {
                    if (this.viewHTML) {
                        this.setContent($(this.original).val());
                        $(this.original).hide();
                    }
                    else {
                        this.saveContent();
                        $(this.original).show();
                    }

                    this.viewHTML = !( this.viewHTML );
                }
            },

            removeFormat : {
                visible : true,
                exec    : function() {
                    this.editorDoc.execCommand('removeFormat', false, []);
                    this.editorDoc.execCommand('unlink', false, []);
                }
            }
        }
    });

    $.extend(Wysiwyg.prototype,
             {
                 original : null,
                 options  : {},

                 element  : null,
                 editor   : null,

                 init : function(element, options) {
                     var self = this;

                     this.editor = element;
                     this.options = options || {};

                     $.data(element, 'wysiwyg', this);

                     var newX = element.width || element.clientWidth;
                     var newY = element.height || element.clientHeight;

                     if (element.nodeName.toLowerCase() == 'textarea') {
                         this.original = element;

                         if (newX == 0 && element.cols) {
                             newX = ( element.cols * 8 ) + 21;
                         }

                         if (newY == 0 && element.rows) {
                             newY = ( element.rows * 16 ) + 16;
                         }

                         var editor = this.editor = $('<iframe></iframe>').css({
                                                                                   minHeight : ( newY - 6 ).toString()
                                                                                       + 'px',
                                                                                   width     : ( newX - 8 ).toString()
                                                                                       + 'px'
                                                                               }).attr('id', $(element).attr('id')
                             + 'IFrame');

                         if ($.browser.msie) {
                             this.editor
                                 .css('height', ( newY ).toString() + 'px');

                             /**
                              var editor = $('<span></span>').css({
                              width     : ( newX - 6 ).toString() + 'px',
                              height    : ( newY - 8 ).toString() + 'px'
                              }).attr('id', $(element).attr('id') + 'IFrame');

                              editor.outerHTML = this.editor.outerHTML;
                              */
                         }
                     }

                     var panel = this.panel = $('<ul></ul>').addClass('panel');

                     this.appendControls();
                     this.element = $('<div></div>').css({
                                                             width : ( newX > 0 ) ? ( newX ).toString() + 'px' : '100%'
                                                         }).addClass('wysiwyg')
                         .append(panel)
                         .append($('<div><!-- --></div>').css({ clear : 'both' }))
                         .append(editor);

                     $(element)
                         // .css('display', 'none')
                         .hide()
                         .before(this.element);

                     this.viewHTML = false;

                     this.initialHeight = newY - 8;

                     /**
                      * @link http://code.google.com/p/jwysiwyg/issues/detail?id=52
                      */
                     this.initialContent = $(element).val();

                     this.initFrame();

                     if (this.initialContent.length == 0) {
                         this.setContent('');
                     }

                     if (this.options.autoSave) {
                         $('form').submit(function() {
                             self.saveContent();
                         });
                     }

                     $('form').bind('reset', function() {
                         self.setContent(self.initialContent);
                         self.saveContent();
                     });
                 },

                 initFrame : function() {
                     var self = this;
                     var style = '';

                     /**
                      * @link http://code.google.com/p/jwysiwyg/issues/detail?id=14
                      */
                     if (this.options.css && this.options.css.constructor == String) {
                         style = '<link rel="stylesheet" type="text/css" media="screen" href="' + this.options.css
                             + '" />';
                     }

                     this.editorDoc = $(this.editor).document();
                     this.editorDoc_designMode = false;

                     try {
                         this.editorDoc.designMode = 'on';
                         this.editorDoc_designMode = true;
                     } catch (e) {
                         // Will fail on Gecko if the editor is placed in an hidden container element
                         // The design mode will be set ones the editor is focused

                         $(this.editorDoc).focus(function() {
                             self.designMode();
                         });
                     }

                     this.editorDoc.open();
                     this.editorDoc.write(
                         this.options.html
                             .replace(/INITIAL_CONTENT/, this.initialContent)
                             .replace(/STYLE_SHEET/, style)
                     );
                     this.editorDoc.close();
                     this.editorDoc.contentEditable = 'true';

                     if ($.browser.msie) {
                         /**
                          * Remove the horrible border it has on IE.
                          */
                         setTimeout(function() {
                             $(self.editorDoc.body).css('border', 'none');
                         }, 0);
                     }

                     $(this.editorDoc).click(function(event) {
                         self.checkTargets(event.target ? event.target : event.srcElement);
                     });

                     /**
                      * @link http://code.google.com/p/jwysiwyg/issues/detail?id=20
                      */
                     $(this.original).focus(function() {
                         $(self.editorDoc.body).focus();
                     });

                     if (this.options.autoSave) {
                         /**
                          * @link http://code.google.com/p/jwysiwyg/issues/detail?id=11
                          */
                         $(this.editorDoc).keydown(function() {
                             self.saveContent();
                         })
                             .keyup(function() {
                                        self.saveContent();
                                    })
                             .mousedown(function() {
                                            self.saveContent();
                                        });
                     }

                     if (this.options.css) {
                         setTimeout(function() {
                             if (self.options.css.constructor == String) {
                                 /**
                                  * $(self.editorDoc)
                                  * .find('head')
                                  * .append(
                                  *     $('<link rel="stylesheet" type="text/css" media="screen" />')
                                  *     .attr('href', self.options.css)
                                  * );
                                  */
                             }
                             else {
                                 $(self.editorDoc).find('body').css(self.options.css);
                             }
                         }, 0);
                     }

                     $(this.editorDoc).keydown(function(event) {
                         if ($.browser.msie && self.options.brIE && event.keyCode == 13) {
                             var rng = self.getRange();
                             rng.pasteHTML('<br />');
                             rng.collapse(false);
                             rng.select();

                             return false;
                         }
                     });
                 },

                 designMode : function() {
                     if (!( this.editorDoc_designMode )) {
                         try {
                             this.editorDoc.designMode = 'on';
                             this.editorDoc_designMode = true;
                         } catch (e) {
                         }
                     }
                 },

                 getSelection : function() {
                     return ( window.getSelection ) ? window.getSelection() : document.selection;
                 },

                 getRange : function() {
                     var selection = this.getSelection();

                     if (!( selection )) {
                         return null;
                     }

                     return ( selection.rangeCount > 0 ) ? selection.getRangeAt(0) : selection.createRange();
                 },

                 getContent : function() {
                     return $($(this.editor).document()).find('body').html();
                 },

                 setContent : function(newContent) {
                     $($(this.editor).document()).find('body').html(newContent);
                 },

                 saveContent : function() {
                     if (this.original) {
                         var content = this.getContent();

                         if (this.options.rmUnwantedBr) {
                             content = ( content.substr(-4) == '<br>' ) ? content.substr(0,
                                                                                         content.length - 4) : content;
                         }

                         $(this.original).val(content);
                     }
                 },

                 appendMenu : function(cmd, args, className, fn) {
                     var self = this;
                     var args = args || [];

                     $('<li></li>').append(
                         $('<a><!-- --></a>').addClass(className || cmd)
                     ).mousedown(
                         function() {
                             if (fn) {
                                 fn.apply(self);
                             } else {
                                 self.editorDoc.execCommand(cmd, false, args);
                             }
                             if (self.options.autoSave) {
                                 self.saveContent();
                             }
                         }).appendTo(this.panel);
                 },

                 appendMenuSeparator : function() {
                     $('<li class="separator"></li>').appendTo(this.panel);
                 },

                 appendControls : function() {
                     for (var name in this.options.controls) {
                         var control = this.options.controls[name];

                         if (control.separator) {
                             if (control.visible !== false) {
                                 this.appendMenuSeparator();
                             }
                         }
                         else if (control.visible) {
                             this.appendMenu(
                                 control.command || name, control.arguments || [],
                                 control.className || control.command || name || 'empty', control.exec
                             );
                         }
                     }
                 },

                 checkTargets : function(element) {
                     for (var name in this.options.controls) {
                         var control = this.options.controls[name];
                         var className = control.className || control.command || name || 'empty';

                         $('.' + className, this.panel).removeClass('active');

                         if (control.tags) {
                             var elm = element;

                             do {
                                 if (elm.nodeType != 1) {
                                     break;
                                 }

                                 if ($.inArray(elm.tagName.toLowerCase(), control.tags) != -1) {
                                     $('.' + className, this.panel).addClass('active');
                                 }
                             } while (elm = elm.parentNode);
                         }

                         if (control.css) {
                             var elm = $(element);

                             do {
                                 if (elm[0].nodeType != 1) {
                                     break;
                                 }

                                 for (var cssProperty in control.css)
                                     if (elm.css(cssProperty).toString().toLowerCase() == control.css[cssProperty]) {
                                         $('.' + className, this.panel).addClass('active');
                                     }
                             } while (elm = elm.parent());
                         }
                     }
                 },

                 getElementByAttributeValue : function(tagName, attributeName, attributeValue) {
                     var elements = this.editorDoc.getElementsByTagName(tagName);

                     for (var i = 0; i < elements.length; i++) {
                         var value = elements[i].getAttribute(attributeName);

                         if ($.browser.msie) {
                             /** IE add full path, so I check by the last chars. */
                             value = value.substr(value.length - attributeValue.length);
                         }

                         if (value == attributeValue) {
                             return elements[i];
                         }
                     }

                     return false;
                 }
             });
})(jQuery);